//package org.jetlinks.community.mstn.aop;
//
//import org.aopalliance.aop.Advice;
//import org.aopalliance.intercept.MethodInvocation;
//import org.hswebframework.web.aop.MethodInterceptorHolder;
//import org.hswebframework.web.authorization.Authentication;
//import org.hswebframework.web.id.IDGenerator;
//import org.hswebframework.web.logger.ReactiveLogger;
//import org.hswebframework.web.logging.AccessLogger;
//import org.hswebframework.web.logging.AccessLoggerInfo;
//import org.hswebframework.web.logging.LoggerDefine;
//import org.hswebframework.web.logging.RequestInfo;
//import org.hswebframework.web.logging.aop.AccessLoggerParser;
//import org.hswebframework.web.logging.aop.ReactiveAopAccessLoggerSupport;
//import org.hswebframework.web.logging.events.AccessLoggerAfterEvent;
//import org.hswebframework.web.logging.events.AccessLoggerBeforeEvent;
//import org.hswebframework.web.utils.ReactiveWebUtils;
//import org.jetlinks.community.mstn.annotation.Tenant;
//import org.jetlinks.community.mstn.logger.TenantLoggerInfo;
//import org.springframework.aop.framework.ReflectiveMethodInvocation;
//import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor;
//import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.context.ApplicationEventPublisher;
//import org.springframework.core.annotation.AnnotationUtils;
//import org.springframework.http.server.reactive.ServerHttpRequest;
//import org.springframework.stereotype.Component;
//import org.springframework.util.ClassUtils;
//import org.springframework.util.ConcurrentReferenceHashMap;
//import org.springframework.web.server.ServerWebExchange;
//import org.springframework.web.server.WebFilter;
//import org.springframework.web.server.WebFilterChain;
//import reactor.core.publisher.Flux;
//import reactor.core.publisher.Mono;
//import reactor.util.context.Context;
//import reactor.util.context.ContextView;
//
//import javax.annotation.Nonnull;
//import java.lang.reflect.Method;
//import java.util.*;
//import java.util.concurrent.ConcurrentHashMap;
//
//@Component
//public class TenantWebFilter extends StaticMethodMatcherPointcutAdvisor implements WebFilter {
//
//    @Autowired(required = false)
//    private final List<AccessLoggerParser> mstnLoggerParsers = new ArrayList();
//    @Autowired
//    private ApplicationEventPublisher eventPublisher;
//    private final Map<CacheKey, LoggerDefine> defineCache = new ConcurrentReferenceHashMap();
//
//    private static final LoggerDefine UNSUPPORTED = new LoggerDefine();
//
//    public TenantWebFilter() {
//
//        this.setAdvice((methodInvocation) -> {
//
//        MethodInterceptorHolder methodInterceptorHolder = MethodInterceptorHolder.create(methodInvocation);
//            TenantLoggerInfo info = this.createLogger(methodInterceptorHolder);
//            Object response = methodInvocation.proceed();
//            if (response instanceof Mono) {
//                return this.wrapMonoResponse((Mono)response, info).contextWrite(Context.of(TenantLoggerInfo.class, info));
//            } else {
//                return response instanceof Flux ? this.wrapFluxResponse((Flux)response, info).contextWrite(Context.of(TenantLoggerInfo.class, info)) : response;
//            }
//        });
//    }
//    @Override
//    public boolean matches(Method method, Class<?> targetClass) {
//        Tenant ann = AnnotationUtils.findAnnotation(method, Tenant.class);
//        return ann != null && this.mstnLoggerParsers.stream().anyMatch((parser) -> {
//            return parser.support(targetClass, method);
//        });
//    }
//
//    @Nonnull
//    @Override
//    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
//        return chain.filter(exchange).contextWrite(Context.of(RequestInfo.class, this.createAccessInfo(exchange)));
//    }
//
//    private RequestInfo createAccessInfo(ServerWebExchange exchange) {
//        RequestInfo info = new RequestInfo();
//        ServerHttpRequest request = exchange.getRequest();
//        info.setRequestId(request.getId());
//        info.setPath(request.getPath().value());
//        info.setRequestMethod(request.getMethodValue());
//        info.setHeaders(request.getHeaders().toSingleValueMap());
//        Optional.ofNullable(ReactiveWebUtils.getIpAddr(request)).ifPresent(info::setIpAddr);
//        return info;
//    }
//
//    protected Mono<?> wrapMonoResponse(Mono<?> mono, AccessLoggerInfo loggerInfo) {
//        return this.wrapFluxResponse(mono.flux(), loggerInfo).singleOrEmpty();
//    }
//
//    protected Flux<?> wrapFluxResponse(Flux<?> flux, AccessLoggerInfo loggerInfo) {
////        return Flux.deferContextual((ctx) -> {
////            Mono var10000 = this.currentRequestInfo(ctx);
////            loggerInfo.getClass();
////            Flux var4 = var10000.doOnNext(loggerInfo::putAccessInfo).then(this.beforeRequest(loggerInfo)).thenMany(flux);
////            loggerInfo.getClass();
////            return var4.doOnError(loggerInfo::setException).doFinally((signal) -> {
////                this.completeRequest(loggerInfo, ctx);
////            });
////        });
//        return Flux.empty();
//    }
//
//    private Mono<Void> beforeRequest(AccessLoggerInfo loggerInfo) {
//        AccessLoggerBeforeEvent event = new AccessLoggerBeforeEvent(loggerInfo);
//        return Authentication.currentReactive().flatMap((auth) -> {
//            loggerInfo.putContext("userId", auth.getUser().getId());
//            loggerInfo.putContext("username", auth.getUser().getUsername());
//            loggerInfo.putContext("userName", auth.getUser().getName());
//            return ReactiveLogger.mdc(new String[]{"userId", auth.getUser().getId(), "username", auth.getUser().getUsername(), "userName", auth.getUser().getName()}).thenReturn(auth);
//        }).then(Mono.defer(() -> {
//            return event.publish(this.eventPublisher);
//        }));
//    }
//
//    private void completeRequest(AccessLoggerInfo loggerInfo, ContextView ctx) {
//        loggerInfo.setResponseTime(System.currentTimeMillis());
//        (new AccessLoggerAfterEvent(loggerInfo)).publish(this.eventPublisher).contextWrite(ctx).subscribe();
//    }
//
//
//    private Mono<RequestInfo> currentRequestInfo(ContextView context) {
//        if (context.hasKey(RequestInfo.class)) {
//            RequestInfo info = (RequestInfo)context.get(RequestInfo.class);
//            ReactiveLogger.log(context, (ctx) -> {
//                info.setContext(new HashMap(ctx));
//            });
//            return Mono.just(info);
//        } else {
//            return Mono.empty();
//        }
//    }
//
//    private LoggerDefine createDefine(MethodInterceptorHolder holder) {
//        return (LoggerDefine)this.mstnLoggerParsers.stream().filter((parser) -> {
//            return parser.support(ClassUtils.getUserClass(holder.getTarget()), holder.getMethod());
//        }).findAny().map((parser) -> {
//            return parser.parse(holder);
//        }).orElse(UNSUPPORTED);
//    }
//
//    protected TenantLoggerInfo createLogger(MethodInterceptorHolder holder) {
//        TenantLoggerInfo info = new TenantLoggerInfo();
//        info.setId((String) IDGenerator.MD5.generate());
//        info.setRequestTime(System.currentTimeMillis());
//        LoggerDefine define = (LoggerDefine)this.defineCache.computeIfAbsent(new CacheKey(ClassUtils.getUserClass(holder.getTarget()), holder.getMethod()), (method) -> {
//            return this.createDefine(holder);
//        });
//        if (define != null) {
//            info.setAction(define.getAction());
//            info.setDescribe(define.getDescribe());
//        }
//
//        Map<String, Object> value = new ConcurrentHashMap();
//        String[] names = holder.getArgumentsNames();
//        Object[] args = holder.getArguments();
//
//        for(int i = 0; i < args.length; ++i) {
//            String name = names[i];
//            Object val = args[i];
//            if (val == null) {
//                value.put(name, "null");
//            } else if (val instanceof Mono) {
//                args[i] = ((Mono)val).doOnNext((param) -> {
//                    value.put(name, param);
//                });
//            } else if (val instanceof Flux) {
//                List<Object> arr = new ArrayList();
//                value.put(name, arr);
//                args[i] = ((Flux)val).doOnNext((param) -> {
//                    arr.add(param);
//                });
//            } else {
//                value.put(name, val);
//            }
//        }
//
//        info.setParameters(value);
//        info.setTarget(holder.getTarget().getClass());
//        info.setMethod(holder.getMethod());
//        return info;
//    }
//
//
//    public int getOrder() {
//        return Integer.MIN_VALUE;
//    }
//
//    @Override
//    public Advice getAdvice() {
//        return super.getAdvice();
//    }
//
//    private static class CacheKey {
//        private Class<?> type;
//        private Method method;
//
//        public CacheKey(Class<?> type, Method method) {
//            this.type = type;
//            this.method = method;
//        }
//
//        public boolean equals(Object o) {
//            if (o == this) {
//                return true;
//            } else if (!(o instanceof CacheKey)) {
//                return false;
//            } else {
//                CacheKey other = (CacheKey)o;
//                if (!other.canEqual(this)) {
//                    return false;
//                } else {
//                    Object this$type = this.type;
//                    Object other$type = other.type;
//                    if (this$type == null) {
//                        if (other$type != null) {
//                            return false;
//                        }
//                    } else if (!this$type.equals(other$type)) {
//                        return false;
//                    }
//
//                    Object this$method = this.method;
//                    Object other$method = other.method;
//                    if (this$method == null) {
//                        if (other$method != null) {
//                            return false;
//                        }
//                    } else if (!this$method.equals(other$method)) {
//                        return false;
//                    }
//
//                    return true;
//                }
//            }
//        }
//
//        protected boolean canEqual(Object other) {
//            return other instanceof CacheKey;
//        }
//
//        public int hashCode() {
//            boolean PRIME = true;
//            int result = 1;
//            Object $type = this.type;
//            result = result * 59 + ($type == null ? 43 : $type.hashCode());
//            Object $method = this.method;
//            result = result * 59 + ($method == null ? 43 : $method.hashCode());
//            return result;
//        }
//    }
//}
